﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#ifndef _UXS_WIN_WINDOW_H
#define _UXS_WIN_WINDOW_H

#pragma once

#include "../Base/OSTypes.h"

#ifdef PLATFORM_WINDOWS_SHELL

#include "WKeyCodes.h"
#include "../Base/WndEvents.h"

#ifdef UX_OPENGL_WINDOW_HANDLES
#	include "../../GL/opengl.h"

	namespace UX_STL {
	namespace winapi {
#		include "../../GL/wglext.h"
	}}
#endif	// UX_OPENGL_WINDOW_HANDLES


namespace UX_STL
{
namespace _platform_
{
	using namespace winapi;
	using namespace UXMath;
	using namespace UXTypesExt;


	//
	// Windows Handles
	//

#ifdef UX_OPENGL_WINDOW_HANDLES
	struct THandles
	{
		HINSTANCE			hInstance;			// Application Instance
		HWND				hWnd;				// Window Handle
		HDC					hDC;				// Device Context
		HGLRC				hRC;				// Rendering Context
		static_string<32>	sClassName;
		///
		THandles(): hInstance(null), hWnd(null), hDC(null), hRC(null), sClassName()
		{}
		
		bool IsValid() const
		{
			return ( hInstance != null and hWnd != null and not sClassName.Empty() );
		}

		bool IsValidGL() const
		{
			return ( hInstance != null and hWnd != null and hDC != null and
					 hRC != null and not sClassName.Empty() );
		}
	};
#else
	struct THandles
	{
		HINSTANCE			hInstance;			// Application Instance
		HWND				hWnd;				// Window Handle
		static_string<32>	sClassName;
		///
		THandles(): hInstance(null), hWnd(null), sClassName()
		{}

		bool IsValid() const
		{
			return ( hInstance != null and hWnd != null and not sClassName.Empty() );
		}
	};
#endif	// UX_OPENGL_WINDOW_HANDLES



	//
	// Window
	//

	class UX_EXPORT CWindow
	{
	// types
	public:
		enum EPause : uint8
		{
			NO_FOCUS	= 0x1,
			USER_PAUSE	= 0x2,
			ON_DESTROY	= 0x4,
		};

		struct TDescriptor
		{
			uvec2				vSize;
			ivec2				vPos;
			e_wnd_style::type	eStyle;
			uint8				uFreq;	// for full screen mode
			bool				bFullScreen;
			bool				bRawInput;
			bool				bCanHideCursor;
			///
			TDescriptor():
				vSize(640,480), vPos(0,0), eStyle(e_wnd_style::DEF_WINDOWED),
				uFreq(0), bFullScreen(false), bRawInput(true), bCanHideCursor(true)
			{}

			explicit
			TDescriptor(const uvec2 &size,
						const ivec2 &pos = ivec2(),
						e_wnd_style::type style = e_wnd_style::DEF_WINDOWED,
						uint8 freq = 0,
						bool fullscreen = false,
						bool rawInput = true,
						bool canHideCursor = true):
				vSize(size), vPos(pos), eStyle(style), uFreq(freq), bFullScreen(fullscreen),
				bRawInput(rawInput), bCanHideCursor(canHideCursor) {}
		};

		struct e_wc_style
		{
			enum	type :	uint8
			{
				WINDOW			= winapi::COLOR_WINDOW,
				WINDOW_FRAME	= winapi::COLOR_WINDOWFRAME,
				GRAY			= winapi::COLOR_GRAYTEXT,
				DEFAULT			= WINDOW_FRAME,
			};
		};

		struct TWndClass
		{
			uni_c_string 		pName;
			const void *		pCursor;
			const void *		pIcon;
			e_wc_style::type	eStyle;
			///
			TWndClass () : pName("DefaultWndClass"), pCursor(null), pIcon(null), eStyle(e_wc_style::DEFAULT) {}
			TWndClass (uni_c_string name, e_wc_style::type style, const void *cursor = null, const void *icon = null) :
				pName(name), pCursor(cursor), pIcon(icon), eStyle(style) {}
		};

		struct TSystemMsg
		{
			UINT	uMsg;
			WPARAM	wParam;
			LPARAM	lParam;
			///
			TSystemMsg () : uMsg(0), wParam(0), lParam(0) {}
			TSystemMsg (UINT msg, WPARAM wparam, LPARAM lparam): uMsg(msg), wParam(wparam), lParam(lparam) {}
		};

		struct TCreateInThreadData
		{
			CWindow	*		pThis;
			TDescriptor		sDesc;
			string			sCaption;
			string			sClassName;
			///
			TCreateInThreadData(): pThis(null) {}
			TCreateInThreadData(CWindow *pthis, const TDescriptor &desc, uni_c_string cap, uni_c_string cname):
				pThis(pthis), sDesc(desc), sCaption(cap), sClassName(cname) {}
		};

		struct TListener
		{
			IWindowEventListener *	pListener;
			e_wnd_event::type		eEvents;
			///
			TListener(IWindowEventListener *listener, e_wnd_event::type events): pListener(listener), eEvents(events) {}
		};
		
		typedef array< TListener >			event_listeners_t;
		typedef array< IWindowUpdater *>	wnd_updaters_t;


	// variables
	protected:
		void			*	_pCursor;
		wnd_updaters_t		_aUpdaters;
		string				_sAppCaption;
		event_listeners_t	_aEvLists;
		THandles			_sHandles;
		TDescriptor			_sDescriptor;
		HKL					_pKeyboardInput;
		uint				_uFPSTimer,
							_uFPSCount,
							_uLastFPS;
		uint8				_uPause;
		bool				_bIsLooping	   : 1,
							_bAllowPause   : 1;
		bool				_bFPSInCaption : 1;


	// methods
	protected:
		void _Update();
		virtual usize  _MsgProc(const TSystemMsg &msg);
		
		bool _Create (const TDescriptor &sDesc, uni_c_string pCaption);
		
		static void		_s_ThreadProc(void *pParam);

		static LRESULT	CALLBACK _s_WinProc(HWND, UINT, WPARAM, LPARAM);

		static bool _s_RegisterClass (THandles &sHandles, const TWndClass &sWndClass);
		static bool _s_UnregisterClass (THandles &sHandles);

	public:
		CWindow();
		virtual ~CWindow();
		
		bool CreateInThread (Thread *pThread, const TDescriptor &sDesc, uni_c_string pCaption = uni_c_string(), const TWndClass &sWndClass = TWndClass());

		virtual bool Create (const TDescriptor &sDesc, uni_c_string pCaption = uni_c_string(), const TWndClass &sWndClass = TWndClass());
		virtual void Destroy ();
		virtual int  Loop ();
		virtual void Quit ();

		const ivec2	GetWindowPos()	const;
		const ivec2	GetWindowSize()	const;
		const irect	GetWindowRect()	const;
		const irect	GetViewRect()	const;

		void AddUpdater (IWindowUpdater *pUpd);
		void RemoveUpdater (IWindowUpdater *pUpd);
		
		void AllowPause (bool bAllow);

		bool ChangeParams (const TDescriptor &sDesc, bool bHidden = false);
		bool SetText (uni_c_string pText) const;
		void GetText (string &sText) const;
		bool Show()			const;
		bool Hide()			const;
		bool IsActive()		const;
		bool IsFocused()	const;
		void SetFocus()		const;

		void ShowFPS (bool bShow);
		void ShowCursor (bool bShow);

		void AddEventListener (IWindowEventListener *pEvList, e_wnd_event::type eEvents);
		void RemoveEventListener (IWindowEventListener *pEvList);
		
		void SendEvent (const TWindowEvent &sEvent);

		// cross-platform access to window handles
		THandles const		&	Handles()		const	{ return _sHandles; }
		THandles			&	HandlesRef()			{ return _sHandles; }

		TDescriptor const	&	Descriptor()	const	{ return _sDescriptor; }
		uvec2 const	&			Size()			const	{ return _sDescriptor.vSize; }
		ivec2 const &			Position()		const	{ return _sDescriptor.vPos; }
		e_wnd_style::type		Style()			const	{ return _sDescriptor.eStyle; }
	};
	


//--------------------------- CWindow -----------------------------//
/*
=================================================
	возвращает позицию окна
=================================================
*/
	inline const ivec2 CWindow::GetWindowPos () const
	{
		RECT	winRect = {0,0,0,0};
		winapi::GetWindowRect( _sHandles.hWnd, &winRect );
		return ivec2( winRect.left, winRect.top );
	}

/*
=================================================
	возвращает размер окна
=================================================
*/
	inline const ivec2 CWindow::GetWindowSize () const
	{
		RECT	winRect		= {0,0,0,0};
		DWORD	dw_style	= GetWindowLongA( _sHandles.hWnd, GWL_STYLE ),
						dw_ex_style	= GetWindowLongA( _sHandles.hWnd, GWL_EXSTYLE );
		
		winapi::GetWindowRect( _sHandles.hWnd, &winRect );
		winapi::AdjustWindowRectEx( &winRect, dw_style, FALSE, dw_ex_style );
		return ivec2( winRect.right - winRect.left, winRect.bottom - winRect.top );
	}
	
/*
=================================================
	возвращает прямоугольник окна 
=================================================
*/
	inline const irect CWindow::GetWindowRect () const
	{
		RECT	winRect	= {0,0,0,0};
		winapi::GetWindowRect( _sHandles.hWnd, &winRect );
		return irect( winRect.left, winRect.top, winRect.right, winRect.bottom );
	}
	
/*
=================================================
	возвращает прямоугольник окна без учета рамки
=================================================
*/
	inline const irect CWindow::GetViewRect () const
	{
		RECT	winRect		= {0,0,0,0};
		DWORD	dw_style	= GetWindowLongA( _sHandles.hWnd, GWL_STYLE ),
				dw_ex_style	= GetWindowLongA( _sHandles.hWnd, GWL_EXSTYLE );
		
		winapi::GetWindowRect( _sHandles.hWnd, &winRect );
		winapi::AdjustWindowRectEx( &winRect, dw_style, FALSE, dw_ex_style );
		return irect( winRect.left, winRect.top, winRect.right, winRect.bottom );
	}

/*
=================================================
	показывает окно
=================================================
*/
	inline bool CWindow::Show () const
	{
		return	SetForegroundWindow( _sHandles.hWnd ) == TRUE and
				ShowWindow( _sHandles.hWnd, SW_SHOWNA ) == TRUE;
	}
	
/*
=================================================
	скрывает окно
=================================================
*/
	inline bool CWindow::Hide () const
	{
		return ShowWindow( _sHandles.hWnd, SW_HIDE ) == TRUE;
	}

/*
=================================================
	устанавливает текст окна
=================================================
*/
	inline bool CWindow::SetText (uni_c_string pText) const
	{
		return winapi::SetWindowText( _sHandles.hWnd, pText.cstr() ) == TRUE;
	}

/*
=================================================
	возвращает текст окна
=================================================
*/
	inline void CWindow::GetText (string &sText) const
	{
		string	tmp;
		tmp.Reserve( 0xFF );
		tmp.SetLength( winapi::GetWindowText( _sHandles.hWnd, tmp.ptr(), (int)tmp.Size() ) );
		tmp.FreeReserve();
		sText.SetString( tmp );
	}

/*
=================================================
	активно ли окно
=================================================
*/
	inline bool CWindow::IsActive () const
	{
		return ( GetActiveWindow() == _sHandles.hWnd );
	}
	
/*
=================================================
	фокус на окне?
=================================================
*/
	inline bool CWindow::IsFocused () const
	{
		return ( GetFocus() == _sHandles.hWnd );
	}
	
/*
=================================================
	установить фокус на окно
=================================================
*/
	inline void CWindow::SetFocus () const
	{
		winapi::SetFocus( _sHandles.hWnd );
	}

/*
=================================================
	
=================================================
*/
	inline void CWindow::AllowPause (bool bAllow)
	{
		_bAllowPause = bAllow;
	}

/*
=================================================
	
=================================================
*/
	inline void CWindow::ShowFPS (bool bShow)
	{
		_bFPSInCaption = bShow;

		if ( !_bFPSInCaption )
			SetText( _sAppCaption.cstr() );
	}
	
/*
=================================================
	
=================================================
*/
	inline void CWindow::ShowCursor (bool bShow)
	{
		if ( not bShow )
			winapi::SetClassLongA( Handles().hWnd, winapi::GCLP_HCURSOR, (winapi::LONG)_pCursor );
		else
			winapi::SetClassLongA( Handles().hWnd, winapi::GCLP_HCURSOR, (winapi::LONG)winapi::LoadCursor( null, winapi::IDC_ARROW ) );
	}

//-------------------------------------------------------------------

}	// _platform_
}	// UX_STL


#endif	// PLATFORM_WINDOWS_SHELL
#endif	// _UXS_WIN_WINDOW_H